/*
 * I2S音频接口实现文件
 * 实现I2S音频输出的核心功能
 */

#include "i2s_audio.h"
#include "esp_log.h"
#include "driver/i2s_std.h"

static const char *TAG = "I2S_AUDIO";
static i2s_chan_handle_t tx_chan = NULL; // I2S发送通道句柄

/**
 * @brief 初始化I2S音频接口
 * @return ESP_OK 初始化成功
 *         其他错误码 初始化失败
 */
esp_err_t i2s_audio_init(void) {
    ESP_LOGI(TAG, "开始初始化I2S音频接口...");
    
    // 1. 配置I2S发送通道
    i2s_chan_config_t chan_cfg = {
        .id = I2S_NUM_0,             // I2S端口号
        .role = I2S_ROLE_MASTER,     // 主模式
        .dma_desc_num = 6,           // DMA描述符数量
        .dma_frame_num = 512,        // 每个DMA描述符的帧数
        .auto_clear = true,          // 自动清除DMA描述符
    };
    
    // 创建I2S发送通道
    // 修复参数顺序：第二个参数是RX通道，第三个参数是TX通道
    // 原代码: esp_err_t ret = i2s_new_channel(&chan_cfg, NULL, &tx_chan);
    esp_err_t ret = i2s_new_channel(&chan_cfg, &tx_chan, NULL);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "创建I2S发送通道失败: %s", esp_err_to_name(ret));
        return ret;
    }
    
    // 2. 配置I2S标准模式
    i2s_std_config_t std_cfg = {
        .clk_cfg = {
            .sample_rate_hz = I2S_SAMPLE_RATE,   // 采样率
            .clk_src = I2S_CLK_SRC_DEFAULT,      // 默认时钟源
            .mclk_multiple = I2S_MCLK_MULTIPLE_256, // MCLK倍数
        },
        .slot_cfg = {
            .data_bit_width = I2S_DATA_BIT_WIDTH_16BIT, // 数据位宽
            .slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT, // 槽位位宽
            .slot_mode = I2S_SLOT_MODE_STEREO,     // 立体声模式
            .slot_mask = I2S_STD_SLOT_BOTH,        // 左右声道都使能
            .ws_width = I2S_DATA_BIT_WIDTH_16BIT,  // WS宽度
            .ws_pol = false,                       // WS极性
            .bit_shift = false,                    // 位移
            // .reverse = false,                     // 删除这一行，ESP-IDF v5.3.1中没有这个成员
            .big_endian = false,                   // 小端模式
        },
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,               // 不使用MCLK
            .bclk = I2S_BCK_PIN,                   // 位时钟引脚，将bck改为bclk
            .ws = I2S_WS_PIN,                      // 字选择引脚
            .dout = I2S_DOUT_PIN,                  // 数据输出引脚
            .din = I2S_GPIO_UNUSED,                // 不使用数据输入
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,                 // 将bck_inv改为bclk_inv
                .ws_inv = false,
            },
        },
    };
    
    // 应用I2S配置
    ret = i2s_channel_init_std_mode(tx_chan, &std_cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "初始化I2S标准模式失败: %s", esp_err_to_name(ret));
        i2s_del_channel(tx_chan);
        tx_chan = NULL;
        return ret;
    }
    
    // 3. 使能I2S通道
    ret = i2s_channel_enable(tx_chan);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "使能I2S通道失败: %s", esp_err_to_name(ret));
        i2s_del_channel(tx_chan);
        tx_chan = NULL;
        return ret;
    }
    
    ESP_LOGI(TAG, "I2S音频接口初始化成功");
    return ESP_OK;
}

/**
 * @brief 销毁I2S音频接口
 * @return ESP_OK 销毁成功
 *         其他错误码 销毁失败
 */
esp_err_t i2s_audio_deinit(void) {
    ESP_LOGI(TAG, "开始销毁I2S音频接口...");
    
    if (tx_chan == NULL) {
        ESP_LOGW(TAG, "I2S通道未初始化");
        return ESP_OK;
    }
    
    // 禁用I2S通道
    esp_err_t ret = i2s_channel_disable(tx_chan);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "禁用I2S通道失败: %s", esp_err_to_name(ret));
    }
    
    // 删除I2S通道
    ret = i2s_del_channel(tx_chan);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "删除I2S通道失败: %s", esp_err_to_name(ret));
        return ret;
    }
    
    tx_chan = NULL;
    ESP_LOGI(TAG, "I2S音频接口销毁成功");
    return ESP_OK;
}

/**
 * @brief 写入音频数据到I2S接口
 * @param data 音频数据缓冲区
 * @param data_len 音频数据长度（字节）
 * @param bytes_written 实际写入的字节数（输出参数）
 * @param timeout_ms 超时时间（毫秒）
 * @return ESP_OK 写入成功
 *         其他错误码 写入失败
 */
esp_err_t i2s_audio_write(const uint8_t *data, size_t data_len, size_t *bytes_written, uint32_t timeout_ms) {
    if (tx_chan == NULL) {
        ESP_LOGE(TAG, "I2S通道未初始化");
        return ESP_ERR_INVALID_STATE;
    }
    
    if (data == NULL) {
        ESP_LOGE(TAG, "无效的音频数据");
        return ESP_ERR_INVALID_ARG;
    }
    
    // 写入音频数据到I2S接口
    esp_err_t ret = i2s_channel_write(tx_chan, data, data_len, bytes_written, timeout_ms);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "写入I2S数据失败: %s", esp_err_to_name(ret));
    }
    
    return ret;
}